/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
class MyPromise {
  constructor(outFuc) {
    try {
      // 执行器将resolvereject暴露出去
      outFuc(this.resolve, this.reject);
    } catch (err) {
      this.reject(err);
    }
  }
  // 状态默认pending
  status = PENDING;
  // 成功值
  value = undefined;
  // 失败值
  reason = undefined;
  // 成功回调数组
  successCallback = [];
  // 失败回调数组
  failCallback = [];

  resolve = (value) => {
    // 如果状态不是pending 直接返回
    if (this.status !== PENDING) return;
    // 改为成功状态
    this.status = FULFILLED;
    // 保存成功得值
    this.value = value;
    // 有成功函数数组并有长度就调用第一个并删除
    while (this.successCallback.length) this.successCallback.shift()(); //shift删除原数组
  };
  reject = (reason) => {
    // 如果状态不是pending 直接返回
    if (this.status !== PENDING) return;
    // 改为失败
    this.status = REJECTED;
    // 保存失败值
    this.reason = reason;
    // 同上
    while (this.failCallback.length) this.failCallback.shift()();
  };
  // 静态方法.resolve
  static resolve(value) {
    if (value instanceof MyPromise) return value; //是promise对象直接返回
    return new MyPromise((fuc) => {
      //返回新promise对象并传入参数
      fuc(value);
    });
  }

  then(successCallback, failCallback) {
    // 若无成功函数或失败就返回一个值或错误error 方便链式调用值得传递
    successCallback = successCallback ? successCallback : (value) => value;
    // 参数可选
    failCallback = failCallback
      ? failCallback
      : (reason) => {
          throw reason;
        };
    let promsie2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          //得包一层不然赋值会出问题
          try {
            let x = successCallback(this.value);
            // 根据返回值判断该怎么返回
            resolvePromise(promsie2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason);
            resolvePromise(promsie2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(promsie2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              resolvePromise(promsie2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return promsie2;
  }
  // finally方法
  finally(callback) {
    //直接调用then方法
    return this.then(
      (value) => {
        // 直接调用callback 并将目前得值暴露出去
        return MyPromise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }
  // catch 。then直接调用失败函数
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }
  // all方法
  static all(array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      //创建调用数组内函数函数防止异步index对不上的问题
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          //判断是否是promise对象
          // 调用数组的promise 对象then
          current.then(
            (value) => addData(i, value),
            (reason) => reject(reason)
          );
        } else {
          // 普通值同步直接塞进数组对应index
          addData(i, array[i]);
        }
      }
    });
  }
}
// 一个调用得判定函数
function resolvePromise(promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    // 防止调用本身不然会进入回调地狱
    return reject(new TypeError("请不要调用请不要该promise本身"));
  }
  if (x instanceof MyPromise) {
    // promise 对象 直接then
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

module.exports = MyPromise;
